package com.ehome.poct.core.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import com.ehome.exception.BizException;
import com.ehome.poct.base.api.dto.device.TestDeviceDTO;
import com.ehome.poct.base.api.dto.doctor.ClinicDTO;
import com.ehome.poct.core.api.common.CellBoardStatus;
import com.ehome.poct.core.api.common.DomainBizCode;
import com.ehome.poct.core.api.dto.check.*;
import com.ehome.poct.core.api.mapping.CheckDataMapping;
import com.ehome.poct.core.api.virtual.CheckService;
import com.ehome.poct.core.api.vo.MaterialBloodBoardVO;
import com.ehome.poct.core.api.vo.check.CellBoardValidVO;
import com.ehome.poct.core.api.vo.check.CheckDataVO;
import com.ehome.poct.core.api.vo.check.CheckProcessResultVO;
import com.ehome.poct.core.api.vo.check.CheckStatusVO;
import com.ehome.poct.core.factory.CheckDataFactory;
import com.ehome.poct.core.repository.CellCheckDataRepository;
import com.ehome.poct.core.repository.check.CellBoardRepository;
import com.ehome.poct.core.repository.checkdata.BldCheckDataRepository;
import com.ehome.poct.core.service.rpc.CellErpRpc;
import org.mapstruct.Mapping;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

/**
 * 血常规检测服务实现
 *
 * @author cq
 * @since 2022/03/02 17:44
 **/
@Service
public class CellCheckServiceImpl implements CheckService, AbstractCheckService {

    @Autowired private CellBoardRepository boardRepo;
    @Autowired private CellAlgoServiceImpl cellAlgoServ;
    @Autowired private CellCheckDataRepository cellDataRepo;
    @Autowired private CheckDataMapping checkDataMapping;
    @Autowired private CellErpRpc cellErpRpc;
    @Autowired private BldCheckDataRepository bldCheckDataRepository;
    @Value("${callBack.url}") private String callBackUrl;
    private final  Integer UNI = 2;
    /**
     * 【检测流程】（一）
     * 检验设备信息 设备是否存在，设备状态是否禁止使用
     *
     * @param sno        设备编号
     * @param deviceType 设备类型
     * @return TestDeviceDTO 检测设备
     */
    @Override
    public void handleDeviceInfo(String sno, Integer deviceType, TestDeviceDTO device) {
        if (null == device) {
            throw new BizException(DomainBizCode.NO_DEVICE);
        }
//        if (1 != device.getStatus()) {
//            throw new BizException(DomainBizCode.DEVICE_NOT_BAND_CLINIC);
//        }
        //其他设备状态校验
    }

    /**
     * 【检测流程】（二）
     * 校验诊所信息
     *
     * @param device   设备
     * @param clinicId 诊所ID
     * @return 诊所信息
     */
    @Override
    public void handleClinicInfo(TestDeviceDTO device, Long clinicId, ClinicDTO clinic) {
        if (null == clinic) {
            throw new BizException(DomainBizCode.CLIENT_NOT_FOUND);
        }
        //校验诊所
//        if (!device.getTermClinicId().equals(clinicId)) {
//            throw new BizException(DomainBizCode.CLINIC_BIND_DEVICE_NOT_SAME);
//        }
        CanContinueCheck(clinic);
    }

    /**
     * 【检测流程】（三）
     * 处理耗材信息
     * 状态变更 或者加工处理
     * @param param   检测参数
     *
     */
    @Override
    public SampleMaterialDTO handleSamplematerial(CheckParamDTO param) {
        var board = cellErpRpc.getMaterialBoardDetail(param.getBoardSno());
        if (null == board) { throw new BizException(DomainBizCode.CELL_SAMPLE_MATERIAL_NOT_EXIST); }
        //校验板子信息
        return BeanUtil.toBean(board,CellBoardDTO.class);
    }

    /**
     * 【检测流程】（四）
     * 远程算法调用
     * @param paramDto
     * @param board
     * @return
     */
    @Override
    public AlgoRequestLog algoProcess (CheckParamDTO paramDto, SampleMaterialDTO board){
        //  进行云端算法分析
         final var algoLog =  cellAlgoServ.callAlgo(CheckDataFactory.toAlgoAnalysisDTO(paramDto, board, callBackUrl));
        // 4. 生成检测数据, 修改计数板状态修改
        if (!algoLog.responseIsSuccess()) {
            throw new BizException(DomainBizCode.CALL_ALGO_ERROR);
        }
        return algoLog;
    }

    /**
     * 【检测流程】（五）
     * 预处理算法处理的数据
     * eg:金标仪 要算法识别 条形码 然后判断是不是公司支持的条码
     * 或者后续 金标仪4。0 之后，卡盒有二维码，也需要算法先处理
     **/
    @Override
    public CheckDataDTO handleAlgoData(CheckParamDTO param, String algoData, TestDeviceDTO device, ClinicDTO clinic,
                                       SampleMaterialDTO sample, AlgoRequestLog algoLog) {
        //变更板子状态 处理算法返回的信息 保存板子数据
        final var data = CheckDataFactory.toCheckData(param, algoLog);
        callAlgoSuccess(data, sample);
        return data;
    }


    @Override
    public CheckProcessResultVO handleCheck (CheckParamDTO param,ClinicDTO clinic,TestDeviceDTO device){
        //【检测流程】（一）检验设备信息
        this.handleDeviceInfo(param.getDeviceSno(),param.getDeviceType(),device);
        //【检测流程】（二）校验诊所信息
        this.handleClinicInfo(device, param.getClinicId(),clinic);
        //【检测流程】（三）处理耗材信息
        CellBoardDTO board = (CellBoardDTO) this.handleSamplematerial(param);
        //【检测流程】（四）远程算法调用
        AlgoRequestLog algoLog =this.algoProcess(param,board);
        //【检测流程】（五） 预处理算法处理的数据
        CellCheckDataDTO checkData= (CellCheckDataDTO) this.handleAlgoData(param,null,device,clinic,board,algoLog);
        return CheckDataFactory.toCellCheckResultVO(device,clinic,board,algoLog,checkData);
    }

    @Override
    public CheckStatusVO pollGetStatus(Long id) {
        return CheckDataFactory.toCheckStatusVO(cellDataRepo.findCacheById(id));
    }

    @Override
    public CheckStatusVO manualGetStatus(Long id) {
        return CheckDataFactory.toCheckStatusVO(cellDataRepo.findById(id));
    }

    @Override
    public boolean updateResult(Long reqId, List<String> msg, String result, String morphology, String chart, String images, Boolean hasZero) {
        return cellDataRepo.updateResult(reqId, msg, result, morphology, chart ,images, hasZero);
    }
    /**
     * 校验板子是否存在
     * @param sno
     * @return
     */
    @Override
    public Boolean  boardIsExist(String sno){
        return null==boardRepo.getByBoardSno(sno)?false:true;
    }

    /**
     * 校验版本 返回板子其他信息
     * @param sno
     * @return
     */
    @Override
    public CellBoardValidVO boardThickness(String sno) {
        MaterialBloodBoardVO  board = cellErpRpc.getMaterialBoardDetail(sno);
       return (null != board)?new CellBoardValidVO(true,board.getBoardThickness(), board.getBoardThickness(),
               board.getRefractiveRate(),board.getBoardThicknessA(),board.getBoardThicknessB(), board.getBoardThicknessC(),board.getBoardSno(),board.getBoardBatch(),board.getRbc(),board.getPlt(),board.getWbc(),board.getHgb())
               :new CellBoardValidVO(false,null,null,null,null,null,null,null,null,null,null,null,null);
    }

    @Override
    public CheckDataVO getCheckData(Long id) {
        return  checkDataMapping.to( cellDataRepo.findById(id));
    }

    /**
     * 是否能进行检测 todo 这个方法要挪到通用帮助类
     * @param ClinicDto 诊所对象
     * @return  false 不可以检测
     */
    public Boolean CanContinueCheck (ClinicDTO ClinicDto){
        //如果账户为特殊账户且effective_start和effective_end为空或者不在时间段内（时间为datetime）则不能做检测，也不能检测分佣不能返佣不能提现
        //如果账户为特殊账户且effective_start和effective_end在时间段内 可以做检测，不能检测分佣，也不能返佣，可以提现
        if (ObjectUtil.isNotEmpty(ClinicDto) && 1 == ClinicDto.getSpecialAccountType()) {
            if (null == ClinicDto.getEffectiveStart() || null == ClinicDto.getEffectiveEnd()) {
                throw new BizException(DomainBizCode.SPECIAL_CLIENT_NOT_EFFECTIVE);
            }
            if(LocalDateTime.now().isAfter(ClinicDto.getEffectiveStart()) && LocalDateTime.now().isBefore(ClinicDto.getEffectiveEnd())) {
                return true;
            }
            throw new BizException(DomainBizCode.SPECIAL_CLIENT_NOT_EFFECTIVE);
        }
        //默认都可以
        return true;
    }

    public void callAlgoSuccess(CellCheckDataDTO data, SampleMaterialDTO sample) {
        cellDataRepo.save(data);
        boardRepo.updateStatus(CellBoardStatus.USED, sample.getBoardSno());
    }

    @Override
    public CheckProcessResultVO saveLocalAlgodata(AlgoLocalDataDTO algoDto) {
        Integer checkType= UNI.equals(algoDto.getType())?121:100;
        var dataDTO = checkDataMapping.toBld(algoDto);
        dataDTO.setCheckType(checkType);
        if(null==dataDTO.getAlgoReqId() || 0==dataDTO.getAlgoReqId()){ dataDTO.setAlgoReqId(IdUtil.getSnowflake().nextId());}
        bldCheckDataRepository.insert(dataDTO);
        boardRepo.updateStatus(CellBoardStatus.USED, algoDto.getBoardSno());
        return new CheckProcessResultVO(dataDTO.getBloodCheckDataId(),Long.valueOf(dataDTO.getCheckType()),"","血常规检测",null);
    }
}
